package bp.difference.handler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import bp.da.DataType;
import bp.difference.ContextHolderUtils;
import bp.difference.SystemConfig;
import bp.web.WebUser;
import bp.web.UserContextHolder;
import bp.wf.httphandler.WF_WorkOpt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;

import bp.wf.httphandler.WF_Comm;

import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

@Controller
@RequestMapping("/WF/Comm")
@ResponseBody
public class WF_Comm_Controller extends HttpHandlerBase {

    private static final Logger logger = LoggerFactory.getLogger(WF_Comm_Controller.class);

    // Substring keywords for operations that do not require token validation.
    private static final Set<String> PUBLIC_OPERATION_KEYWORDS = new HashSet<>(Arrays.asList(
            "login",
            "index",
            "selectoneorg_init",
            "getorgbyno",
            "getorgs",
            "admiin",
            "dbinstall",
            "getdingtalkuserinfo",
            "autologinwithry",
            "default_initry",
            "auto_login",
            "getdingcorpid",
            "getwxappletuserinfo",
            "default_logout",
            "checkencryptenable",
            "getpasswordencryptiontype",
            "getisencryptionkey",
            "ccbpmservices",
            "pcandmobileurl",
            "scanguide_init_vue3",
            "do_init",
            "port_init",
            "portsaas_init",
            "getuserinfo",
            "getgzuserinfo",
            "user_orgnos",
            "sso_init",
            "sso_callback",
            "sso_selectuctoken",
            "sso_jumplogin",
            "sso_logout",
            "sso_signlogin",
            "sso_loginbyst"
    ));
    /**
     * 默认执行的方法
     *
     * @return
     */
//    @RequestMapping(value = "/ProcessRequest")
//    public void ProcessRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//        SystemConfig.setIsBSsystem(true);
//        //处理Token信息
//        try {
//            DealToken();
//        }catch (Exception e) {
//            PrintWriter out = null;
//            try{
//                response.setHeader("content-type", "text/html;charset=UTF-8");
//                response.setCharacterEncoding("UTF-8");
//                out = this.getResponse().getWriter();
//                out.write(e.getMessage());
//            }catch(Exception ex){
//            }finally{
//                if(out!=null)
//                    out.close();
//            }
//        }
//        response.reset();
//        response.setCharacterEncoding("UTF-8");
//        WF_Comm CommHandler = new WF_Comm();
//        if (request instanceof DefaultMultipartHttpServletRequest) {
//            //如果是附件上传Request，则将该Request放入全局Request。为了解决springmvc中全局Request无法转化为附件Request
//            HttpServletRequest request1 = CommonUtils.getRequest();
//            request1.setAttribute("multipartRequest", request);
//        }
//        super.ProcessRequestPost(CommHandler);
//    }

    @RequestMapping(value = "/ProcessRequest")
    public void ProcessRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        SystemConfig.setIsBSsystem(true);

        // 处理Token信息
        try {
            DealToken();
        } catch (Exception e) {
            // 使用参数中的response对象，而不是通过getResponse()获取
            response.setContentType("text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 适当的状态码

            try (PrintWriter out = response.getWriter()) {
                out.write(e.getMessage());
            }
            // 切记，如果已经写入了错误响应，就提前返回，不要继续处理
            return;
        }

        // 只有在token处理成功后才设置响应编码
        response.setCharacterEncoding("UTF-8");

        // 处理文件上传请求
        if (request instanceof DefaultMultipartHttpServletRequest) {
            HttpServletRequest requestContext = CommonUtils.getRequest();
            requestContext.setAttribute("multipartRequest", request);
        }

        // 统一构建请求内的用户上下文（无侵入，仅本地 ThreadLocal），请求结束清理。
        try {
            UserContextHolder.set(bp.web.WebUser.buildContextFromRequest());
            // 使用相同的response对象
            super.ProcessRequestPost(new WF_Comm(), request, response);
        } finally {
            UserContextHolder.clear();
        }
    }

    @Override
    public Class<WF_Comm> getCtrlType() {
        return WF_Comm.class;
    }

    private void DealToken() throws Exception {
        // Resolve operation type
        String doMethod = this.GetRequestVal("DoMethod");
        String doType = this.getDoType();
        if ("HttpHandler".equals(doType)) {
            doType = doMethod;
        }
        if (DataType.IsNullOrEmpty(doType)) {
            throw new RuntimeException("err@没有获取到执行的方法");
        }

        String op = doType.toLowerCase(Locale.ROOT);
        if (shouldBypassTokenCheck(op)) {
            if (logger.isInfoEnabled()) {
                logger.info("DealToken: bypass for operation='{}'", op);
            }
            return; // public operation, no token required
        }

        // Normalize token
        String token = this.GetRequestVal("Token");
        if (!DataType.IsNullOrEmpty(token)) {
            token = token.trim();
            if ("undefined".equalsIgnoreCase(token) || "null".equalsIgnoreCase(token)) {
                token = "";
            }
        }

        if (!DataType.IsNullOrEmpty(token)) {
            // Expose token to request attribute so downstream token lookups in this request can see it.
            try { this.getRequest().setAttribute("Token", token); } catch (Exception ignore) {}
            try { bp.web.UserContextHolder.setRequestToken(token); } catch (Exception ignore) {}

            // If cookie carries an old token, remove its Redis snapshot to avoid duplications.
            try {
                javax.servlet.http.Cookie ck = ContextHolderUtils.getCookie("Token");
                if (ck != null) {
                    String oldToken = ck.getValue();
                    if (!DataType.IsNullOrEmpty(oldToken) && !oldToken.equals(token) && SystemConfig.getRedisIsEnable()) {
                        try {
                            bp.web.WebUser.getWebUser_Redis().remove("WebUser_" + oldToken);
                            if (logger.isInfoEnabled()) {
                                logger.info("DealToken: removed old Redis snapshot for token={} (new={})", maskToken(oldToken), maskToken(token));
                            }
                        } catch (Exception ignore2) {}
                    }
                }
            } catch (Exception ignore) {}

            if (logger.isInfoEnabled()) {
                logger.info("DealToken: locking request token={} for operation='{}'", maskToken(token), op);
            }

            bp.wf.Dev2Interface.Port_LoginByToken(token);
            return;
        }

        if (DataType.IsNullOrEmpty(WebUser.getNo())) {
            throw new Exception("err@登录信息丢失，请重新登录");
        }
    }

    private boolean shouldBypassTokenCheck(String opLowerCase) {
        for (String kw : PUBLIC_OPERATION_KEYWORDS) {
            if (opLowerCase.contains(kw)) return true;
        }
        return false;
    }

    private String maskToken(String token) {
        if (DataType.IsNullOrEmpty(token)) return "";
        int len = token.length();
        if (len <= 8) return "****";
        return token.substring(0,4) + "****" + token.substring(len-4);
    }


}
